using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;

using SC.Infrastructure.Persistence.Interfaces;

using Sc.Domain.Interfaces;

namespace SC.Infrastructure.Persistence
{
	class Repository<T> : IRepository<T> where T : class, IEntity
	{
		private readonly UnitOfWork _context;
		private readonly IDbSet<T> _entitySet;

		public Repository(UnitOfWork context, IDbSet<T> entitySet)
		{
			_context = context;
			_entitySet = entitySet;
		}

		#region Implementation of IEnumerable

		/// <summary>
		/// Returns an enumerator that iterates through the collection.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
		/// </returns>
		/// <filterpriority>1</filterpriority>
		public IEnumerator<T> GetEnumerator()
		{
			return _entitySet.AsEnumerable().GetEnumerator();
		}

		/// <summary>
		/// Returns an enumerator that iterates through a collection.
		/// </summary>
		/// <returns>
		/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
		/// </returns>
		/// <filterpriority>2</filterpriority>
		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		#endregion

		#region Implementation of ICollection<T>

		/// <summary>
		/// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> 
		/// is read-only.</exception>
		public void Add(T item)
		{
			_entitySet.Add(item);
		}

		/// <summary>
		/// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> 
		/// is read-only. </exception>
		public void Clear()
		{
			foreach (var entity in _entitySet)
			{
				_entitySet.Remove(entity);
			}
		}

		/// <summary>
		/// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"/> contains a specific value.
		/// </summary>
		/// <returns>
		/// true if <paramref name="item"/> is found in the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false.
		/// </returns>
		/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
		public bool Contains(T item)
		{
			return _entitySet.Contains(item);
		}

		/// <summary>
		/// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, 
		/// starting at a particular <see cref="T:System.Array"/> index.
		/// </summary>
		/// <param name="array">The one-dimensional <see cref="T:System.Array"/> 
		/// that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. 
		/// The <see cref="T:System.Array"/> must have zero-based indexing.</param><param name="arrayIndex">
		/// The zero-based index in <paramref name="array"/> at which copying begins.
		/// </param><exception cref="T:System.ArgumentNullException"><paramref name="array"/> is null.
		/// </exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="arrayIndex"/> 
		/// is less than 0.</exception><exception cref="T:System.ArgumentException"><paramref name="array"/> 
		/// is multidimensional.-or-The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"/> 
		/// is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.
		/// -or-Type <paramref name="T"/> cannot be cast automatically to the type of the destination <paramref name="array"/>.</exception>
		public void CopyTo(T[] array, int arrayIndex)
		{
			foreach (var entity in _entitySet)
			{
				array[arrayIndex++] = entity;
			}
		}

		/// <summary>
		/// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <returns>
		/// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; 
		/// otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original 
		/// <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </returns>
		/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> 
		/// is read-only.</exception>
		public bool Remove(T item)
		{
			_entitySet.Remove(item);
			return true;
		}

		/// <summary>
		/// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <returns>
		/// The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </returns>
		public int Count
		{
			get
			{
				return _entitySet.Count();
			}
		}
		/// <summary>
		/// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
		/// </summary>
		/// <returns>
		/// true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.
		/// </returns>
		public bool IsReadOnly
		{
			get
			{
				return true;
			}
		}

		#endregion

		#region Implementation of IRepository<T>

		public IEnumerable<T> Query(Func<T, bool> predicate)
		{
			return _entitySet.Where(predicate);
		}

		public T GetSingle(Func<T, bool> predicate)
		{
			return Query(predicate).FirstOrDefault();
		}

		public void Update(T entity)
		{
			_context.InnerContext.Entry(entity).State = EntityState.Modified;
		}

		public void Attach(T entity)
		{
			_entitySet.Attach(entity);
		}

		public void InserOrUpdate(T entity)
		{
			_context.InnerContext.Entry(entity).State = entity.Id == 0 ?
								   EntityState.Added :
								   EntityState.Modified;
		}

		#endregion
	}
}